home *** CD-ROM | disk | FTP | other *** search
/ SuperHack / SuperHack CD.bin / CODING / WINDOWS / NTFILSRC.ZIP / GUI / FILEMON.C < prev    next >
Encoding:
C/C++ Source or Header  |  1996-10-27  |  21.8 KB  |  781 lines

  1. /******************************************************************************
  2. *
  3. *       FileMon - File System Monitor for Windows NT
  4. *        
  5. *        Copyright (c) 1996 Mark Russinovich and Bryce Cogswell
  6. *
  7. *        You have the right to take this code and use it in whatever 
  8. *       way you wish.
  9. *
  10. *        PROGRAM: FileMon.c
  11. *
  12. *        PURPOSE: Communicates with the FileMon driver to display 
  13. *        file system activity information.
  14. *
  15. ******************************************************************************/
  16. #define UNICODE 1
  17. #include <windows.h>    // includes basic windows functionality
  18. #include <windowsx.h>
  19. #include <commctrl.h>   // includes the common control header
  20. #include <stdio.h>
  21. #include <string.h>
  22. #include <winioctl.h>
  23. #include "resource.h"
  24. #include "ioctlcmd.h"
  25.  
  26. #include "instdrv.h"
  27.  
  28. // Set this to 0 for a non-processor specific version that does not get
  29. // process names from the device driver
  30. #define GETPROCESS 1
  31.  
  32. // Variables/definitions for the driver that performs the actual monitoring.
  33. #define                SYS_FILE        TEXT("FILEMON.SYS")
  34. #define                SYS_NAME        TEXT("FILEMON")
  35. static HANDLE        sys_handle        = INVALID_HANDLE_VALUE;
  36.  
  37.  
  38. // Drive type names
  39. #define DRVUNKNOWN        0
  40. #define DRVFIXED        1
  41. #define DRVREMOTE        2
  42. #define DRVRAM            3
  43. #define DRVCD            4
  44. #define DRVREMOVE        5
  45. TCHAR DrvNames[][32] = {
  46.     TEXT("UNKNOWN"),
  47.     TEXT("FIXED"),
  48.     TEXT("REMOTE"),
  49.     TEXT("RAM"),
  50.     TEXT("CD"),
  51.     TEXT("REMOVEABLE"),
  52. };    
  53.  
  54.  
  55. // Buffer into which driver can copy statistics
  56. char                Stats[ MAX_STORE ];
  57. // Current fraction of buffer filled
  58. DWORD                StatsLen;
  59.  
  60. // Application instance handle
  61. HINSTANCE            hInst;
  62.  
  63. // Misc globals
  64. HWND                hWndList;
  65. BOOLEAN                Capture = TRUE;
  66. BOOLEAN                Autoscroll = TRUE;
  67.  
  68. // For info saving
  69. TCHAR                szFileName[256];
  70. BOOLEAN                FileChosen = FALSE;
  71.  
  72. // General buffer for storing temporary strings
  73. static TCHAR        msgbuf[ 257 ];
  74.  
  75. // General cursor manipulation
  76. HCURSOR             hSaveCursor;
  77. HCURSOR             hHourGlass;
  78.  
  79. // procs
  80. long APIENTRY         MainWndProc( HWND, UINT, UINT, LONG );
  81. BOOL APIENTRY         About( HWND, UINT, UINT, LONG );
  82.  
  83. //functions
  84. BOOL                 InitApplication( HANDLE );
  85. HWND                 InitInstance( HANDLE, int );
  86. DWORD                 Hook_Drives( HMENU DriveMenu, DWORD MaxDriveSet, DWORD CurDriveSet ); 
  87. HWND                 CreateList( HWND );
  88. void                 UpdateStatistics( HWND hWnd, HWND hWndList, BOOL Clear );
  89. int                 CALLBACK ListCompareProc( LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort );
  90. void                SaveFile( HWND hDlg, HWND listbox, BOOLEAN SaveAs );
  91.  
  92.  
  93. /******************************************************************************
  94. *
  95. *    FUNCTION:    Abort:
  96. *
  97. *    PURPOSE:    Handles emergency exit conditions.
  98. *
  99. *****************************************************************************/
  100. void Abort( HWND hWnd, TCHAR * Msg )
  101. {
  102.     UnloadDeviceDriver( SYS_NAME );
  103.     MessageBox( hWnd, Msg, TEXT("FileMon"), MB_OK );
  104.     PostQuitMessage( 1 );
  105. }        
  106.  
  107.  
  108. /****************************************************************************
  109. *
  110. *    FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)
  111. *
  112. *    PURPOSE:    calls initialization function, processes message loop
  113. *
  114. ****************************************************************************/
  115. int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
  116.                         LPSTR lpCmdLine, int nCmdShow )
  117. {
  118.     MSG     msg;      
  119.     HWND    hWnd;
  120.         
  121.     if ( ! InitApplication( hInstance ) )
  122.         return FALSE;     
  123.  
  124.     // initializations that apply to a specific instance 
  125.     if ( (hWnd = InitInstance( hInstance, nCmdShow )) == NULL )
  126.         return FALSE;
  127.  
  128.  
  129.     // acquire and dispatch messages until a WM_QUIT message is received.
  130.     while ( GetMessage( &msg, NULL, 0, 0 ) )  {
  131.         TranslateMessage( &msg );
  132.         DispatchMessage( &msg ); 
  133.     }
  134.     return msg.wParam;                                         
  135. }
  136.  
  137.  
  138. /****************************************************************************
  139. *
  140. *    FUNCTION: InitApplication(HANDLE)
  141. *
  142. *    PURPOSE: Initializes window data and registers window class
  143. *
  144. ****************************************************************************/
  145. BOOL InitApplication( HANDLE hInstance )
  146. {
  147.     WNDCLASS  wc;
  148.     
  149.     // Fill in window class structure with parameters that describe the
  150.     // main (statistics) window. 
  151.     wc.style            = 0;                     
  152.     wc.lpfnWndProc        = (WNDPROC)MainWndProc; 
  153.     wc.cbClsExtra        = 0;              
  154.     wc.cbWndExtra        = 0;              
  155.     wc.hInstance        = hInstance;       
  156.     wc.hIcon            = LoadIcon( hInstance, TEXT("APPICON") );
  157.     wc.hCursor            = LoadCursor( NULL, IDC_ARROW );
  158.     wc.hbrBackground    = GetStockObject( LTGRAY_BRUSH ); 
  159.     wc.lpszMenuName        = TEXT("LISTMENU");  
  160.     wc.lpszClassName    = TEXT("FileMonClass");
  161.     if ( ! RegisterClass( &wc ) )
  162.         return FALSE;
  163.  
  164.     return TRUE;
  165. }
  166.  
  167.  
  168. /****************************************************************************
  169. *
  170. *    FUNCTION:  InitInstance(HANDLE, int)
  171. *
  172. *    PURPOSE:  Saves instance handle and creates main window
  173. *
  174. ****************************************************************************/
  175. HWND InitInstance( HANDLE hInstance, int nCmdShow )
  176. {
  177.     HWND hWndMain;
  178.  
  179.     hInst = hInstance;
  180.  
  181.     hWndMain = CreateWindow( TEXT("FileMonClass"), TEXT("NT File Monitor"), 
  182.                             WS_OVERLAPPEDWINDOW,
  183.                             CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
  184.                             NULL, NULL, hInstance, NULL );
  185.  
  186.     // if window could not be created, return "failure" 
  187.     if ( ! hWndMain )
  188.         return NULL;
  189.     
  190.     // make the window visible; update its client area; and return "success"
  191.     ShowWindow( hWndMain, nCmdShow );
  192.     UpdateWindow( hWndMain ); 
  193.     return hWndMain;      
  194. }
  195.  
  196.  
  197. /****************************************************************************
  198. *
  199. *    FUNCTION: MainWndProc(HWND, unsigned, WORD, LONG)
  200. *
  201. *    PURPOSE:  Processes messages for the statistics window.
  202. *
  203. ****************************************************************************/
  204. LONG APIENTRY MainWndProc( HWND hWnd, UINT message, UINT wParam, LONG lParam) 
  205. {
  206.     static DWORD    CurDriveSet = 0;
  207.     static DWORD    MaxDriveSet = 0;
  208.     static HMENU    DriveMenu;
  209.     ULONG            irpcount;
  210.     DWORD            nb;
  211.     DWORD            drive, drivetype;
  212.     TCHAR            Path[ 256 ];
  213.     TCHAR             name[32];
  214.  
  215.     switch ( message ) {
  216.  
  217.         case WM_CREATE:
  218.  
  219.             // get hourglass icon ready
  220.             hHourGlass = LoadCursor( NULL, IDC_WAIT );
  221.  
  222.             // post hourglass icon
  223.             SetCapture(hWnd);
  224.             hSaveCursor = SetCursor(hHourGlass);
  225.  
  226.             // Create the ListBox within the main window
  227.             hWndList = CreateList( hWnd );
  228.             if ( hWndList == NULL )
  229.                 MessageBox( NULL, TEXT("List not created!"), NULL, MB_OK );
  230.  
  231.             // open the handle to the device
  232.             GetCurrentDirectory( sizeof Path, Path );
  233.             wsprintf( Path+lstrlen(Path), TEXT("\\%s"), SYS_FILE );
  234.             if ( ! LoadDeviceDriver( SYS_NAME, Path, &sys_handle ) )  {
  235.                 wsprintf( msgbuf, TEXT("Opening %s (%s): error %d"), SYS_NAME, Path,
  236.                                 GetLastError( ) );
  237.                 Abort( hWnd, msgbuf );
  238.             }
  239.  
  240.             // Have driver zero information
  241.             if ( ! DeviceIoControl(    sys_handle, FILEMON_zerostats,
  242.                                     NULL, 0, NULL, 0, &nb, NULL ) )
  243.             {
  244.                 Abort( hWnd, TEXT("Couldn't access device driver") );
  245.                 return TRUE;
  246.             }
  247.  
  248.             // Create a pop-up menu item with the drives
  249.             DriveMenu = CreateMenu();
  250.  
  251.             // Get available drives we can monitor
  252.             MaxDriveSet = GetLogicalDrives();
  253.             CurDriveSet = MaxDriveSet;
  254.             for ( drive = 0; drive < 32; ++drive )  {
  255.                 if ( MaxDriveSet & (1 << drive) )  {
  256.                     wsprintf( name, TEXT("%c:\\"), 'A'+drive );
  257.                     switch ( GetDriveType( name ) )  {
  258.                         // We don't like these: remove them
  259.                         case 0:                    // The drive type cannot be determined.
  260.                         case 1:                    // The root directory does not exist.
  261.                             drivetype = DRVUNKNOWN;
  262.                             CurDriveSet &= ~(1 << drive);
  263.                             break;
  264.                         case DRIVE_REMOVABLE:    // The drive can be removed from the drive.
  265.                             drivetype = DRVREMOVE;
  266.                             CurDriveSet &= ~(1 << drive);
  267.                             break;
  268.                         case DRIVE_CDROM:        // The drive is a CD-ROM drive.
  269.                             drivetype = DRVCD;
  270.                             CurDriveSet &= ~(1 << drive);
  271.                             break;
  272.  
  273.                         // We like these types
  274.                         case DRIVE_FIXED:        // The disk cannot be removed from the drive.
  275.                             drivetype = DRVFIXED;
  276.                             break;
  277.                         case DRIVE_REMOTE:        // The drive is a remote (network) drive.
  278.                             drivetype = DRVREMOTE;
  279.                             break;
  280.                         case DRIVE_RAMDISK:        // The drive is a RAM disk.
  281.                             drivetype = DRVRAM;
  282.                             break;
  283.                     }
  284.                     wsprintf( name, TEXT("Drive &%c: (%s)"), 'A'+drive, DrvNames[drivetype] );
  285.                     InsertMenu( DriveMenu, 0xFFFFFFFF, MF_BYPOSITION|MF_STRING,
  286.                                 IDC_DRIVE+drive, name );
  287.                 }
  288.             }
  289.             // Insert into top-level menu
  290.             InsertMenu( GetMenu( hWnd ), 2, MF_BYPOSITION|MF_POPUP|MF_STRING,
  291.                         (UINT)DriveMenu, TEXT("&Drives") );
  292.  
  293.             // Have driver hook the selected drives
  294.             CurDriveSet = Hook_Drives( DriveMenu, MaxDriveSet, CurDriveSet );
  295.  
  296.             // Start up timer to periodically update screen
  297.             SetTimer( hWnd,    1, 500/*ms*/, NULL );
  298.             
  299.             // Initialization done
  300.             SetCursor( hSaveCursor );
  301.             ReleaseCapture();
  302.             return 0;
  303.  
  304.         case WM_NOTIFY:
  305.             // Make sure its intended for us
  306.             if ( wParam == ID_LIST )  {
  307.                 NM_LISTVIEW    * pNm = (NM_LISTVIEW *)lParam;
  308.                 switch ( pNm->hdr.code )  {
  309.  
  310.                     case LVN_BEGINLABELEDIT:
  311.                         // Don't allow editing of information
  312.                         return TRUE;
  313.                 }
  314.             }
  315.             return 0;
  316.  
  317.         case WM_COMMAND:
  318.  
  319.             switch ( LOWORD( wParam ) )     {
  320.  
  321.                 // stats related commands to send to driver
  322.                 case IDM_CLEAR:
  323.                     // Have driver zero information
  324.                     if ( ! DeviceIoControl(    sys_handle, FILEMON_zerostats,
  325.                                             NULL, 0, NULL, 0, &nb, NULL ) )
  326.                     {
  327.                         Abort( hWnd, TEXT("Couldn't access device driver") );
  328.                         return TRUE;
  329.                     }
  330.                     // Update statistics windows
  331.                     UpdateStatistics( hWnd, hWndList, TRUE );
  332.                     return 0;
  333.  
  334.                 case IDM_CAPTURE:
  335.                     // Read statistics from driver
  336.                     Capture = !Capture;
  337.                     CheckMenuItem( GetMenu(hWnd), IDM_CAPTURE,
  338.                                     MF_BYCOMMAND|(Capture?MF_CHECKED:MF_UNCHECKED) ); 
  339.                     return 0;
  340.  
  341.                 case IDM_AUTOSCROLL:
  342.                     Autoscroll = !Autoscroll;
  343.                     CheckMenuItem( GetMenu(hWnd), IDM_AUTOSCROLL,
  344.                                     MF_BYCOMMAND|(Autoscroll?MF_CHECKED:MF_UNCHECKED) ); 
  345.                     return 0;
  346.  
  347.                 case IDM_EXIT:
  348.                     // Close ourself
  349.                     SendMessage( hWnd, WM_CLOSE, 0, 0 );
  350.                     return 0;
  351.  
  352.                 case IDM_ABOUT:
  353.                     // Show the names of the authors
  354.                     DialogBox( hInst, TEXT("AboutBox"), hWnd, (DLGPROC)About );
  355.                     return 0;
  356.  
  357.                 case IDM_SAVE:
  358.                     SaveFile( hWnd, hWndList, FALSE );
  359.                     return 0;
  360.  
  361.                 case IDM_SAVEAS:
  362.                     SaveFile( hWnd, hWndList, TRUE );
  363.                     return 0;
  364.  
  365.                 default:
  366.                     drive = LOWORD( wParam ) - IDC_DRIVE;
  367.                     if ( drive < 32 )  {
  368.                         // Toggle status
  369.                         CurDriveSet ^= (1 << drive);
  370.                         // Have driver hook the selected drives
  371.                         CurDriveSet = Hook_Drives( DriveMenu, MaxDriveSet, CurDriveSet );
  372.                         return 0;
  373.                     } else {
  374.                         // Default behavior
  375.                         return DefWindowProc( hWnd, message, wParam, lParam );
  376.                     }
  377.             }
  378.             break;
  379.  
  380.         case WM_TIMER:
  381.             // Time to query the device driver for more data
  382.             if ( Capture )  {
  383.                 for (;;)  {
  384.                     // Have driver fill Stats buffer with information
  385.                     if ( ! DeviceIoControl(    sys_handle, FILEMON_getstats,
  386.                                             NULL, 0, &Stats, sizeof Stats,
  387.                                             &StatsLen, NULL ) )
  388.                     {
  389.                         Abort( hWnd, TEXT("Couldn't access device driver") );
  390.                         return TRUE;
  391.                     }
  392.                     if ( StatsLen == 0 )
  393.                         break;
  394.                     // Update statistics windows
  395.                     UpdateStatistics( hWnd, hWndList, FALSE );
  396.                 }
  397.             }
  398.             return 0;
  399.  
  400.         case WM_SIZE:
  401.             // Move or resize the List
  402.             MoveWindow( hWndList, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE );
  403.             return 0;
  404.  
  405.         case WM_CLOSE:
  406.             // see if the driver can unload
  407.             if ( ! DeviceIoControl(    sys_handle, FILEMON_unloadquery,
  408.                                     NULL, 0, NULL, 0,
  409.                                     &irpcount, NULL ) ) {
  410.                 Abort( hWnd, TEXT("Couldn't access device driver") );
  411.                 return TRUE;
  412.             }
  413.             if( irpcount ) {
  414.                 wsprintf( msgbuf,     TEXT("The Filemon device driver cannot unload\n")
  415.                                     TEXT("at this time due to oustanding requests.\n\n")
  416.                                     TEXT("Do you wish to exit the GUI now?"));
  417.                 if( MessageBox( hWnd, msgbuf, TEXT("Filemon"), MB_ICONSTOP|MB_YESNO ) == IDNO )
  418.                     return 0;
  419.                 CloseHandle( sys_handle );
  420.             } else {
  421.                 CloseHandle( sys_handle );            
  422.                 if ( ! UnloadDeviceDriver( SYS_NAME ) )  {
  423.                     wsprintf( msgbuf, TEXT("Error unloading \"%s\""), SYS_NAME );
  424.                     MessageBox( hWnd, msgbuf, TEXT("FileMon"), MB_OK );
  425.                 }
  426.             }
  427.             return DefWindowProc( hWnd, message, wParam, lParam );
  428.  
  429.         case WM_DESTROY:
  430.             PostQuitMessage(0);
  431.             return 0;
  432.  
  433.         default:
  434.             // Default behavior
  435.             return DefWindowProc( hWnd, message, wParam, lParam );
  436.     }
  437.     return 0;
  438. }
  439.  
  440.  
  441. /******************************************************************************
  442. *
  443. *    FUNCTION:    Hook_Drives
  444. *
  445. *    PURPOSE:    Hook the currently selected drives, updating menu checks
  446. *
  447. ******************************************************************************/
  448. DWORD Hook_Drives( HMENU DriveMenu, DWORD MaxDriveSet, DWORD CurDriveSet ) 
  449. {
  450.     DWORD nb;
  451.     DWORD drive;
  452.  
  453.     // Tell device driver which drives to monitor
  454.     if ( ! DeviceIoControl(    sys_handle, FILEMON_setdrives,
  455.                             &CurDriveSet, sizeof CurDriveSet,
  456.                             &CurDriveSet, sizeof CurDriveSet,
  457.                             &nb, NULL ) )
  458.         return 0;
  459.  
  460.     // Update menu items
  461.     for ( drive = 0; drive < 32; ++drive )
  462.         if ( MaxDriveSet & (1<<drive) )  {
  463.             if ( CurDriveSet & (1<<drive) )
  464.                 CheckMenuItem( DriveMenu, IDC_DRIVE+drive, MF_BYCOMMAND|MF_CHECKED );
  465.             else
  466.                 CheckMenuItem( DriveMenu, IDC_DRIVE+drive, MF_BYCOMMAND|MF_UNCHECKED );
  467.         }
  468.     return CurDriveSet;
  469. }
  470.  
  471.  
  472. /******************************************************************************
  473. *
  474. *    FUNCTION:    Split
  475. *
  476. *    PURPOSE:    Split a delimited line into components
  477. *
  478. ******************************************************************************/
  479. int Split( char * line, char delimiter, char * items[] )
  480. {
  481.     int        cnt = 0;
  482.  
  483.     for (;;)  {
  484.         // Add prefix to list of components        
  485.         items[cnt++] = line;
  486.  
  487.         // Check for more components
  488.         line = strchr( line, delimiter );
  489.         if ( line == NULL )
  490.             return cnt;
  491.  
  492.         // Terminate previous component and move to next
  493.         *line++ = '\0';
  494.     }        
  495. }
  496.  
  497.  
  498. /******************************************************************************
  499. *
  500. *    FUNCTION:    ListAppend
  501. *
  502. *    PURPOSE:    Add a new line to List window
  503. *
  504. ******************************************************************************/
  505. BOOL List_Append( HWND hWndList, DWORD seq, char * line )
  506. {
  507.     LV_ITEM        lvI;    // list view item structure
  508.     int            row;
  509.     char    *    items[20];
  510.     int            itemcnt = 0;
  511.  
  512.     // Split line into columns
  513.     itemcnt = Split( line, '\t', items );
  514.     if ( itemcnt == 0 )
  515.         return TRUE;
  516.  
  517.     // Determine row number for request
  518.     if ( *items[0] )  {
  519.         // Its a new request.  Put at end.
  520.         row = 0x7FFFFFFF;
  521.     } else {
  522.         // Its a status.  Locate its associated request.
  523.         lvI.mask = LVIF_PARAM;
  524.         lvI.iSubItem = 0;
  525.         for ( row = ListView_GetItemCount(hWndList) - 1; row >= 0; --row )  {
  526.             lvI.iItem = row;
  527.             if ( ListView_GetItem( hWndList, &lvI )  &&  (DWORD)lvI.lParam == seq )
  528.                 break;
  529.         }
  530.         if ( row == -1 )
  531.             // No request associated with status.
  532.             return TRUE;
  533.     }
  534.  
  535.     // Sequence number if a new item
  536.     if ( *items[0] )  {
  537.         wsprintf( msgbuf, TEXT("%d"), seq );
  538.         lvI.mask        = LVIF_TEXT | LVIF_PARAM;
  539.         lvI.iItem        = row;
  540.         lvI.iSubItem    = 0;
  541.         lvI.pszText        = msgbuf;
  542.         lvI.cchTextMax    = lstrlen( lvI.pszText ) + 1;
  543.         lvI.lParam        = seq;
  544.         row = ListView_InsertItem( hWndList, &lvI );
  545.         if ( row == -1 )  {
  546.             wsprintf( msgbuf, TEXT("Error adding item %d to list view"), seq );
  547.             MessageBox( hWndList, msgbuf, TEXT("FileMon Error"), MB_OK );
  548.             return FALSE;
  549.         }
  550.     }
  551. #if GETPROCESS
  552.     // Process name
  553.     if ( itemcnt>0 && *items[0] ) {
  554.         OemToChar( items[0], msgbuf );
  555.         ListView_SetItemText( hWndList, row, 1, msgbuf );
  556.     }
  557. #endif
  558.     // Request type
  559.     if ( itemcnt>1 && *items[1] )  {
  560.         OemToChar( items[1], msgbuf );
  561.         ListView_SetItemText( hWndList, row, 1+GETPROCESS, msgbuf );
  562.     }
  563.  
  564.     // Path
  565.     if ( itemcnt>2 && *items[2] )  {
  566.         OemToChar( items[2], msgbuf );
  567.         ListView_SetItemText( hWndList, row, 2+GETPROCESS, msgbuf );
  568.     }
  569.  
  570.     // Result
  571.     if ( itemcnt>4 && *items[4] )  {
  572.         OemToChar( items[4], msgbuf );
  573.         ListView_SetItemText( hWndList, row, 3+GETPROCESS, msgbuf );
  574.     }
  575.  
  576.     // Additional
  577.     if ( itemcnt>3 && *items[3] )  {
  578.         OemToChar( items[3], msgbuf );
  579.         ListView_SetItemText( hWndList, row, 4+GETPROCESS, msgbuf );
  580.     }
  581.  
  582.     return TRUE;
  583. }
  584.  
  585.  
  586. /******************************************************************************
  587. *
  588. *    FUNCTION:    UpdateStatistics
  589. *
  590. *    PURPOSE:    Clear the statistics window and refill it with the current 
  591. *                contents of the statistics buffer.  Does not refresh the 
  592. *                buffer from the device driver.
  593. *
  594. ******************************************************************************/
  595. void UpdateStatistics( HWND hWnd, HWND hWndList, BOOL Clear )
  596. {
  597.     PENTRY    ptr;
  598.  
  599.     // Just return if nothing to do
  600.     if ( !Clear  &&  StatsLen < sizeof(int)+2 )
  601.         return;
  602.  
  603.     // Start with empty list
  604.     if ( Clear ) 
  605.         ListView_DeleteAllItems( hWndList );
  606.  
  607.  
  608.     // Add all List items from Stats[] data
  609.     for ( ptr = (void *)Stats; (char *)ptr < Stats+StatsLen; )  {
  610.          // Add to list
  611.         ULONG len = strlen(ptr->text);
  612.         List_Append( hWndList, ptr->seq, ptr->text );
  613.         ptr = (void *)(ptr->text + len + 1);
  614.     }
  615.  
  616.     // Empty the buffer
  617.     StatsLen = 0;
  618.  
  619.     // Scroll so newly added items are visible
  620.     if ( Autoscroll ) 
  621.         ListView_EnsureVisible( hWndList, ListView_GetItemCount(hWndList)-1, FALSE ); 
  622. }
  623.  
  624.  
  625. /****************************************************************************
  626. *    FUNCTION: CreateListView(HWND)
  627. *
  628. *    PURPOSE:  Creates the statistics list view window and initializes it
  629. *
  630. ****************************************************************************/
  631. HWND CreateList( HWND hWndParent )                                     
  632. {
  633.     HWND        hWndList;              // handle to list view window
  634.     RECT        rc;                   // rectangle for setting size of window
  635.     LV_COLUMN    lvC;                // list view column structure
  636.     DWORD        j;
  637.     static struct {
  638.         TCHAR *    Label;    // title of column
  639.         DWORD    Width;    // width of column in pixels
  640.         DWORD    Fmt;
  641.     } column[] = {
  642.         {    TEXT("#"),            35        },
  643. #if GETPROCESS
  644.         {    TEXT("Process"),    100        },
  645. #endif
  646.         {    TEXT("Request"),    150        },
  647.         {    TEXT("Path"),        200        },
  648.         {    TEXT("Result"),        70        },
  649.         {    TEXT("Other"),        150        },
  650.     };
  651.  
  652.     // Ensure that the common control DLL is loaded.
  653.     InitCommonControls();
  654.  
  655.     // Get the size and position of the parent window.
  656.     GetClientRect( hWndParent, &rc );
  657.  
  658.     // Create the list view window
  659.     hWndList = CreateWindowEx( 0L, WC_LISTVIEW, TEXT(""), 
  660.                                 WS_VISIBLE | WS_CHILD | WS_BORDER | LVS_REPORT |
  661.                                     WS_EX_CLIENTEDGE,    // styles
  662.                                 0, 0, rc.right - rc.left, rc.bottom - rc.top,
  663.                                 hWndParent,    (HMENU)ID_LIST, hInst, NULL );
  664.     if ( hWndList == NULL )
  665.         return NULL;
  666.  
  667.     // Initialize columns
  668.     lvC.mask    = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
  669.     lvC.fmt        = LVCFMT_LEFT;    // left-align column
  670.  
  671.     // Add the columns.
  672.     for ( j = 0; j < sizeof column/sizeof column[0]; ++j )  {
  673.         lvC.iSubItem    = j;
  674.         lvC.cx            = column[j].Width;
  675.          lvC.pszText        = column[j].Label;
  676.         if ( ListView_InsertColumn( hWndList, j, &lvC ) == -1 )
  677.             return NULL;
  678.     }
  679.  
  680.     return hWndList;
  681. }
  682.  
  683.  
  684. /****************************************************************************
  685. *    FUNCTION: SaveFile()
  686. *
  687. *    PURPOSE:  Lets the user go select a file.
  688. *
  689. ****************************************************************************/
  690. void SaveFile( HWND hWnd, HWND ListBox, BOOLEAN SaveAs )
  691. {
  692.     OPENFILENAME    SaveFileName;
  693.     TCHAR            szFile[256] = TEXT(""), fieldtext[256], output[1024];
  694.     FILE            *hFile;
  695.     int                numitems;
  696.     int                row, subitem;
  697.  
  698.     if( SaveAs || !FileChosen ) {
  699.         SaveFileName.lStructSize       = sizeof (SaveFileName);
  700.         SaveFileName.hwndOwner         = hWnd;
  701.         SaveFileName.hInstance         = (HANDLE) hInst;
  702.         SaveFileName.lpstrFilter       = TEXT("File Info (*.FIL)\0*.FIL\0All (*.*)\0*.*\0");
  703.         SaveFileName.lpstrCustomFilter = (LPTSTR)NULL;
  704.         SaveFileName.nMaxCustFilter    = 0L;
  705.         SaveFileName.nFilterIndex      = 1L;
  706.         SaveFileName.lpstrFile         = szFile;
  707.         SaveFileName.nMaxFile          = 256;
  708.         SaveFileName.lpstrFileTitle    = NULL;
  709.         SaveFileName.nMaxFileTitle     = 0;
  710.         SaveFileName.lpstrInitialDir   = NULL;
  711.         SaveFileName.lpstrTitle        = TEXT("Save File Info...");
  712.         SaveFileName.nFileOffset       = 0;
  713.         SaveFileName.nFileExtension    = 0;
  714.         SaveFileName.lpstrDefExt       = TEXT("*.fil");
  715.         SaveFileName.lpfnHook           = NULL;
  716.          SaveFileName.Flags = OFN_LONGNAMES|OFN_HIDEREADONLY;
  717.  
  718.         if( !GetSaveFileName( &SaveFileName )) 
  719.             return;
  720.     } else 
  721.         // open previous szFile
  722.         wcscpy( szFile, szFileName );
  723.  
  724.     // open the file
  725.     hFile = _wfopen( szFile, TEXT("w") );
  726.     if( !hFile ) {
  727.         MessageBox(    NULL, TEXT("Create File Failed."),
  728.                 TEXT("Save Error"), MB_OK|MB_ICONSTOP );
  729.         return;
  730.     }
  731.  
  732.     // post hourglass icon
  733.     SetCapture(hWnd);
  734.     hSaveCursor = SetCursor(hHourGlass);
  735.  
  736.     numitems = ListView_GetItemCount(ListBox);
  737.     for ( row = 0; row < numitems; row++ )  {
  738.         output[0] = 0;
  739.         for( subitem = 0; subitem < 6; subitem++ ) {
  740.             fieldtext[0] = 0;
  741.             ListView_GetItemText( ListBox, row, subitem, fieldtext, 256 );
  742.             wcscat( output, fieldtext );
  743.             wcscat( output, TEXT("\t") );
  744.         }
  745.         fwprintf( hFile, TEXT("%s\n"), output );
  746.     }
  747.     fclose( hFile );
  748.     wcscpy( szFileName, szFile );
  749.     FileChosen = TRUE;
  750.     SetCursor( hSaveCursor );
  751.     ReleaseCapture(); 
  752. }
  753.  
  754.  
  755.  
  756. /****************************************************************************
  757. *
  758. *    FUNCTION:    About
  759. *
  760. *    PURPOSE:    Processes messages for "About" dialog box
  761. *
  762. ****************************************************************************/
  763.  
  764. BOOL APIENTRY About( HWND hDlg, UINT message, UINT wParam, LONG lParam )
  765. {
  766.     switch ( message )  {
  767.        case WM_INITDIALOG:
  768.           return TRUE;
  769.  
  770.        case WM_COMMAND:              
  771.           if ( LOWORD( wParam ) == IDOK )     {
  772.               EndDialog( hDlg, TRUE );
  773.               return TRUE;
  774.           }
  775.           break;
  776.     }
  777.     return FALSE;   
  778. }
  779.